/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.meta.rpc.table;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.base.Strings;
import com.je.common.base.DynaBean;
import com.je.common.base.constants.ConstantVars;
import com.je.common.base.db.JEDatabase;
import com.je.common.base.exception.PlatformException;
import com.je.common.base.exception.PlatformExceptionEnum;
import com.je.common.base.mapper.query.NativeQuery;
import com.je.common.base.service.CommonService;
import com.je.common.base.service.MetaResourceService;
import com.je.common.base.service.MetaService;
import com.je.common.base.service.db.PcDBMethodServiceFactory;
import com.je.common.base.util.DataBaseUtils;
import com.je.common.base.util.JEUUID;
import com.je.common.base.util.StringUtil;
import com.je.meta.model.view.ViewColumn;
import com.je.meta.model.view.ViewDdlVo;
import com.je.meta.model.view.ViewOuput;
import org.apache.servicecomb.provider.pojo.RpcReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;

/**
 * @program: jecloud-meta
 * @author: LIULJ
 * @create: 2021-09-18 13:26
 * @description:
 */
@Service
public class MetaViewRpcServiceImpl implements MetaViewRpcService {

    @RpcReference(microserviceName = "meta", schemaId = "metaViewRpcService")
    private MetaViewRpcService metaViewRpcService;
    @Autowired
    private MetaService metaService;
    @Autowired
    private CommonService commonService;
    @Autowired
    private MetaTableRpcService metaTableRpcService;
    @Autowired
    private MetaResourceService metaResourceService;

    /**
     * 创建视图
     *
     * @param table
     * @return
     */
    @Override
    public DynaBean createView(DynaBean table) {
        String sql = table.getStr("RESOURCETABLE_SQL");
        String tableCode = table.getStr("RESOURCETABLE_TABLECODE");
        table.setStr("RESOURCETABLE_OLDTABLECODE", table.getStr("RESOURCETABLE_TABLECODE"));
        //创建视图
        //TODO 不同数据库不同的创建方法
//        metaService.executeSql(" CREATE VIEW " + tableCode + " AS " + sql);
        metaService.executeSql(sql);
        clearMyBatisFuncCache(tableCode);
        return table;
    }

    public void clearMyBatisFuncCache(String tableCode){
        List<DynaBean>  funcinfoList = metaResourceService.selectByTableCodeAndNativeQuery("JE_CORE_FUNCINFO", NativeQuery.build().eq("FUNCINFO_TABLENAME",tableCode));
        for(DynaBean funcinfo:funcinfoList){
            metaService.clearMyBatisFuncCache(funcinfo.getStr("FUNCINFO_FUNCCODE"));
        }
    }
    /**
     * 持久视图信息
     *
     * @param table  表信息
     * @param fields 字段
     * @return
     */
    @Override
    public DynaBean saveViewInfo(DynaBean table, String fields) {
        return metaViewRpcService.saveViewInfo(table, fields);
    }

    /**
     * 更新视图级联信息
     *
     * @param table
     * @param cascade
     * @return
     */
    @Override
    public DynaBean updateViewCascadeInfo(DynaBean table, String cascade) {
        return metaViewRpcService.saveViewInfo(table, cascade);
    }

    /**
     * 创建表和视图的关系
     *
     * @param tableInfoList
     * @param resourcetableTablecode
     */
    @Override
    public void saveTableView(JSONArray tableInfoList, String resourcetableTablecode) {
        metaViewRpcService.saveTableView(tableInfoList, resourcetableTablecode);
    }

    /**
     * 修改视图
     *
     * @param table 修改视图的信息
     * @return
     */
    @Override
    public DynaBean updateView(DynaBean table) {
        String sql = table.getStr("RESOURCETABLE_SQL");
        //sql = sql.substring(sql.indexOf("SELECT"));
        String tableCode = table.getStr("RESOURCETABLE_TABLECODE");
        table.setStr("RESOURCETABLE_OLDTABLECODE", table.getStr("RESOURCETABLE_TABLECODE"));
        String dbUpdateSql = PcDBMethodServiceFactory.getPcDBMethodService().getUpdateView();
        if (dbUpdateSql.contains("VIEW")) {
            dbUpdateSql = dbUpdateSql.replace("VIEW", "");
        }
        sql = sql.trim();
        String checkStr = sql.toUpperCase().substring(0, 10);
        if (checkStr.contains("CREATE")) {
            sql = sql.replaceFirst("CREATE", dbUpdateSql);
        }
        //metaService.executeSql(dbUpdateSql + " " + tableCode + " AS " + sql);
        metaService.executeSql(sql);
        return table;
    }

    /**
     * 持久视图信息
     *
     * @param table    视图信息
     * @param fields   保存的列信息
     * @param syncView 是否同步视图，是则不修改功能信息和键信息   只同步列信息
     * @return
     */
    @Override
    public DynaBean updateViewInfo(DynaBean table, String fields, Boolean syncView) {
       DynaBean dynaBean = metaViewRpcService.updateViewInfo(table, fields, syncView);
        clearMyBatisFuncCache(table.getStr("RESOURCETABLE_TABLECODE"));
        return dynaBean;
    }

    /**
     * 生成视图ddl
     *
     * @param viewColumnList
     * @param viewOuputList
     * @param viewCode
     * @param masterTable
     * @return
     */
    @Override
    public StringBuilder generateViewDdl(List<ViewColumn> viewColumnList, List<ViewOuput> viewOuputList, String viewCode, String masterTable) {
        return metaViewRpcService.generateViewDdl(viewColumnList, viewOuputList, viewCode, masterTable);
    }

    /**
     * 创建资源表中视图表数据
     *
     * @param dynaBean
     * @return
     */
    @Override
    public int saveViewDdl(DynaBean dynaBean) {
        return metaViewRpcService.saveViewDdl(dynaBean);
    }

    @Override
    public DynaBean saveTableByView(DynaBean dynaBean, String strData) {
        return metaViewRpcService.saveTableByView(dynaBean, strData);
    }

    @Override
    public DynaBean updateViews(DynaBean dynaBean, String strData) {
        return metaViewRpcService.updateViews(dynaBean, strData);
    }

    @Override
    public DynaBean updateViewCascadeInfos(DynaBean dynaBean, List<ViewColumn> viewColumnList) {
        return metaViewRpcService.updateViewCascadeInfos(dynaBean, viewColumnList);
    }

    @Override
    public DynaBean saveViewInfos(DynaBean dynaBean, List<ViewOuput> viewOuputList, DynaBean table) {
        return metaViewRpcService.saveViewInfos(dynaBean, viewOuputList, table);
    }

    @Override
    public void saveTableViews(List<String> tableInfoList, String tableCode, boolean b) {
        metaViewRpcService.saveTableViews(tableInfoList, tableCode, b);
    }

    @Override
    public DynaBean updateViewInfos(DynaBean table, List<ViewOuput> viewOuputList, boolean b, DynaBean dynaBean) {
        String tableCode = table.getStr("RESOURCETABLE_TABLECODE");
        DynaBean recourceTable = new DynaBean("JE_CORE_RESOURCETABLE", false);
        recourceTable.set("RESOURCETABLE_TABLECODE", tableCode);
        table.set("RESOURCETABLE_ISCREATE", "1");
        if (JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_ORACLE)
                || JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_SHENTONG)
                || JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_KINGBASEES)
                || JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_DM)) {
            recourceTable = DataBaseUtils.getViewBaseInfo(recourceTable, metaService.getConnection());
        } else if (JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_SQLSERVER)) {
            recourceTable = DataBaseUtils.getViewBaseInfoBySQLServer(recourceTable, metaService.getConnection());
        } else if (JEDatabase.getCurrentDatabase().equals(ConstantVars.STR_MYSQL)) {
            recourceTable = DataBaseUtils.getViewBaseInfoByMySQL(recourceTable, metaService.getConnection());
        } else if (JEDatabase.getCurrentDatabase().equals(ConstantVars.STR_TIDB)) {
            recourceTable = DataBaseUtils.getViewBaseInfoByMySQL(recourceTable, metaService.getConnection());
        }
        return metaViewRpcService.updateViewInfos(table, viewOuputList, b, recourceTable);
    }

    @Override
    @Transactional
    public DynaBean selectTableByView(String strData, DynaBean dynaBean) {
        buildViewTableId(dynaBean);
        ViewDdlVo viewDdlVo = ViewDdlVo.build(strData);
        List<ViewOuput> viewOuputList = viewDdlVo.getViewOuput();
        List<ViewColumn> viewColumnList = viewDdlVo.getViewColumn();
        String masterTableId = viewDdlVo.getMasterTableId();
        if(!checkViewDDL(dynaBean.getStr("RESOURCETABLE_SQL"))){
            throw  new PlatformException("视图DDL错误，请排查！", PlatformExceptionEnum.UNKOWN_ERROR);
        }
        ;
        /**
         * 保存视图信息到资源表
         */
        String uuid = JEUUID.uuid();
        dynaBean.setStr(dynaBean.getPkCode(),uuid);
        metaViewRpcService.saveViewDdl(dynaBean);
        dynaBean = createView(dynaBean);
        dynaBean = metaViewRpcService.updateViewCascadeInfos(dynaBean, viewColumnList);
        dynaBean.set("masterTableId", masterTableId);

        DynaBean recourceTable = new DynaBean("JE_CORE_RESOURCETABLE", false);
        recourceTable.set("RESOURCETABLE_TABLECODE", dynaBean.get("RESOURCETABLE_TABLECODE"));
        recourceTable.set("RESOURCETABLE_COORDINATE", dynaBean.getStr("RESOURCETABLE_COORDINATE"));
        if (JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_ORACLE)
                || JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_SHENTONG)
                || JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_KINGBASEES)
                || JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_DM)) {
            recourceTable = DataBaseUtils.getViewBaseInfo(recourceTable, metaService.getConnection());
        } else if (JEDatabase.getCurrentDatabase().equalsIgnoreCase(ConstantVars.STR_SQLSERVER)) {
            recourceTable = DataBaseUtils.getViewBaseInfoBySQLServer(recourceTable, metaService.getConnection());
        } else if (JEDatabase.getCurrentDatabase().equals(ConstantVars.STR_MYSQL)) {
            recourceTable = DataBaseUtils.getViewBaseInfoByMySQL(recourceTable, metaService.getConnection());
        } else if (JEDatabase.getCurrentDatabase().equals(ConstantVars.STR_TIDB)) {
            recourceTable = DataBaseUtils.getViewBaseInfoByMySQL(recourceTable, metaService.getConnection());
        }

        dynaBean = metaViewRpcService.saveViewInfos(dynaBean, viewOuputList, recourceTable);
        //加载视图字段
        String masterTable = getTableCodeByTableIdOfViewDdVo(viewColumnList, masterTableId);
        List<String> tableInfoList = getTableList(viewOuputList, masterTable);

        metaViewRpcService.saveTableViews(tableInfoList, dynaBean.getTableCode(), false);
        clearMyBatisFuncCache(dynaBean.getStr("RESOURCETABLE_TABLECODE"));
        return dynaBean;
    }

    private boolean checkViewDDL(String createViewDDL) {
        String selectSql = getSelectSqlByCreateViewDDL(createViewDDL);
        try{
            metaService.selectSql(selectSql);
            return true;
        }catch (Exception e){
            return false;
        }
    }

    private String getSelectSqlByCreateViewDDL(String createViewDDL) {
        createViewDDL = createViewDDL.toUpperCase();
        int index = createViewDDL.indexOf(" AS ");
        return createViewDDL.substring(index+3,createViewDDL.length());
    }

    public void buildResourceTable(DynaBean dynaBean, DynaBean parentTable) {
        if (!StringUtil.isEmpty(dynaBean.get("RESOURCETABLE_TYPE"))) {
            if ("MODULE".equals(dynaBean.get("RESOURCETABLE_TYPE"))) {
                dynaBean.set("SY_NODETYPE", "GENERAL");
            } else {
                dynaBean.set("SY_NODETYPE", "LEAF");
            }
        }

        dynaBean.set("SY_JECORE", "0");
        dynaBean.set("SY_JESYS", "1");
    }

    @Override
    @Transactional
    public DynaBean updateTableByView(String strData, DynaBean dynaBean) {
        buildViewTableId(dynaBean);
        ViewDdlVo viewDdlVo = JSONObject.parseObject(strData, ViewDdlVo.class);
        List<ViewOuput> viewOuputList = viewDdlVo.getViewOuput();
        List<ViewColumn> viewColumnList = viewDdlVo.getViewColumn();
        String masterTableId = viewDdlVo.getMasterTableId();

        if(!checkViewDDL(dynaBean.getStr("RESOURCETABLE_SQL"))){
            throw  new PlatformException("视图DDL错误，请排查！", PlatformExceptionEnum.UNKOWN_ERROR);
        }

        dynaBean = updateView(dynaBean);
        dynaBean.set("masterTableId", masterTableId);
        dynaBean = updateViewInfos(dynaBean, viewOuputList, false, null);
        dynaBean = metaViewRpcService.updateViewCascadeInfos(dynaBean, viewColumnList);
        String masterTable = getTableCodeByTableIdOfViewDdVo(viewColumnList, masterTableId);
        List<String> tableInfoList = getTableList(viewOuputList, masterTable);
        metaViewRpcService.saveTableViews(tableInfoList, dynaBean.getTableCode(), true);
        return dynaBean;
    }

    /**
     * 解析视图位置信息，将视图所用的表，存储起来，删除表的时候添加判断是否可以删除
     */
    private void buildViewTableId(DynaBean dynaBean) {
        String coordinate = dynaBean.getStr("RESOURCETABLE_COORDINATE");
        if (!Strings.isNullOrEmpty(coordinate)) {
            try {
                StringBuffer tableCodeSb = new StringBuffer();
                JSONArray tables = JSONArray.parseArray(coordinate);
                for (Object table : tables) {
                    JSONObject tablejo = (JSONObject) table;
                    tableCodeSb.append(tablejo.getString("tableId") + ",");
                }
                dynaBean.setStr("RESOURCETABLE_TABLESINFO", tableCodeSb.toString());
            } catch (Exception e) {
                throw new PlatformException("视图位置信息解析失败", PlatformExceptionEnum.JE_CORE_TABLE_ERROR);
            }
        }
    }

    @Override
    public DynaBean selectOneByCode(DynaBean dynaBean) {
        return metaViewRpcService.selectOneByCode(dynaBean);
    }

    /**
     * 根据指定主键获取主表名称在VIEWVO返回封装类中
     *
     * @param viewColumnList
     * @param masterTableId
     * @return
     */
    public String getTableCodeByTableIdOfViewDdVo(List<ViewColumn> viewColumnList, String masterTableId) {
        String masterTable = null;
        for (int i = 0; i < viewColumnList.size(); i++) {
            ViewColumn viewColumn = viewColumnList.get(i);
            if (viewColumn.getMainColumn().equals(masterTableId)) {
                masterTable = viewColumn.getMainTableCode();
                return masterTable;
            }
            if (viewColumn.getTargetColumn().equals(masterTableId)) {
                masterTable = viewColumn.getTargetTableCode();
                return masterTable;
            }
        }
        return masterTable;
    }

    /**
     * 获取所有子表
     *
     * @param ouputListList
     * @param masterTable
     * @return
     */
    private List<String> getTableList(List<ViewOuput> ouputListList, String masterTable) {
        List<String> tableList = new ArrayList<String>(ouputListList.size());
        for (int i = 0, size = ouputListList.size(); i < size; i++) {
            ViewOuput viewOuput = ouputListList.get(i);
            String tableCode = viewOuput.getTableCode();
            if (!(tableCode.equals(masterTable))) {
                if (tableList.contains(tableCode)) {
                    continue;
                }
                tableList.add(tableCode);
            }

        }
        return tableList;
    }

}
